package com.leyou.search.service.impl;

import com.leyou.common.exception.LyException;
import com.leyou.item.client.ItemClient;
import com.leyou.search.dto.SearchRequest;
import com.leyou.search.entity.Goods;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.service.SearchService;
import com.leyou.starter.elastic.entity.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ItemClient itemClient;

    @Override
    public Mono<List<String>> getSuggest(String key) {

        return goodsRepository.suggestBySingleField("suggestion", key);

    }

    @Override
    public Mono<PageInfo<Goods>> listData(SearchRequest searchRequest) {

        QueryBuilder queryBuilder = getQueryBuilder(searchRequest);

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 添加查询条件
        sourceBuilder.query(queryBuilder);

        int size = searchRequest.getRows();

        Integer page = searchRequest.getPage() == null ? 1 : searchRequest.getPage();

        int from = ( page - 1 ) * size;

        sourceBuilder.from(from);

        sourceBuilder.size(size);

        HighlightBuilder highlighterBuilder = new HighlightBuilder();

        highlighterBuilder.preTags("<am>");

        highlighterBuilder.postTags("</am>");

        highlighterBuilder.field("title");

        sourceBuilder.highlighter(highlighterBuilder);

        // 动态排序条件
        String sortBy = searchRequest.getSortBy();

//        if (StringUtils.isNoneBlank(sortBy)) {}
//        if (null != sortBy && !(("").length() == (sortBy).length())) {
        if (null != sortBy && !("").equals(sortBy)) {

            sourceBuilder.sort(
                    SortBuilders.fieldSort(sortBy).
                            order(searchRequest.getDesc()?SortOrder.DESC:SortOrder.ASC));

        }

        return this.goodsRepository.queryBySourceBuilderForPageHighlight(sourceBuilder);
    }

    private QueryBuilder getQueryBuilder(SearchRequest searchRequest) {

        String key = searchRequest.getKey();

        if (StringUtils.isEmpty(key)) {

            throw new LyException(HttpStatus.BAD_REQUEST.value(),"请求参数有误!");

        }

        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 给查询构造器添加查询条件
        boolQueryBuilder.must(QueryBuilders.matchQuery("title",key).operator(Operator.AND));

        //获取过滤条件，如果过滤条件不为空，则需要给查询构造条件，循环添加过滤条件
        Map<String, String> filters = searchRequest.getFilters();

        if (!CollectionUtils.isEmpty(filters)) {

            filters.entrySet().forEach(entry -> {

                String entryKey = entry.getKey();

                String entryValue = entry.getValue();

                if ("分类".equals(entryKey)) {

                    entryKey = "categoryId";

                    boolQueryBuilder.filter(QueryBuilders.termQuery(entryKey,entryValue));

                } else if ("品牌".equals(entryKey)) {

                    entryKey = "brandId";

                    boolQueryBuilder.filter(QueryBuilders.termQuery(entryKey,entryValue));

                }

            });

        }

        // 添加查询条件
        return boolQueryBuilder;

    }

    @Override
    public Mono<Map<String, List<?>>> listFilter(SearchRequest searchRequest) {

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 添加查询条件
        QueryBuilder queryBuilder = getQueryBuilder(searchRequest);

        sourceBuilder.query(queryBuilder);

        sourceBuilder.aggregation(AggregationBuilders.terms("brandAggregation").field("brandId").size(30));

        sourceBuilder.aggregation(AggregationBuilders.terms("categoryAggregation").field("categoryId").size(30));

        // 过滤条件的聚合以及过滤条件值的聚合
        sourceBuilder.aggregation(
                AggregationBuilders.nested("specAgg","specs")
                        .subAggregation(AggregationBuilders.terms("nameAgg").field("specs.name")
                                .subAggregation(AggregationBuilders.terms("valueAgg").field("specs.value"))));

        Mono<Aggregations> aggregationsMono = this.goodsRepository.aggregationBySourceBuilder(sourceBuilder);

        return aggregationsMono.map(aggregations -> {

            //过滤条件的封装集合，使用LinkedHashMap是因为是有序的，根据你放入的顺序决定的
            // 这里是  分类在前，品牌在后
            Map<String, List<?>> map = new LinkedHashMap<>();

            // 解析品牌聚合，获取到品牌id后，根据品牌id集合查询对应的品牌集合
            Terms brandAggregation = aggregations.get("brandAggregation");

            List<Long> brandIds = brandAggregation
                    .getBuckets()
                    .stream()
                    .map(bucket -> ((Terms.Bucket) bucket).getKeyAsNumber())
                    .map(Number::longValue)
                    .collect(Collectors.toList());

            // 解析分类聚合，获取到分类id后，根据分类id集合查询对应的分类集合
            Terms categoryAggregation = aggregations.get("categoryAggregation");

            List<Long> categoryIds = categoryAggregation
                    .getBuckets()
                    .stream()
                    .map(bucket -> ((Terms.Bucket) bucket).getKeyAsNumber())
                    .map(Number::longValue)
                    .collect(Collectors.toList());

            if (!CollectionUtils.isEmpty(categoryIds)) {

                map.put("分类",this.itemClient.queryCategoryByIds(categoryIds));

            }

            if (!CollectionUtils.isEmpty(brandIds)) {

                map.put("品牌",this.itemClient.queryBrandByIds(brandIds));

            }

            // 解析规格参数的nested的聚合
            Nested specAgg = aggregations.get("specAgg");

            // 获取名称聚合
            Terms nameAgg = specAgg.getAggregations().get("nameAgg");

            nameAgg.getBuckets().forEach(bucket->{

                // 获取每一个名称值
                String key = ((Terms.Bucket) bucket).getKeyAsString();

                // 获取当前名称聚合的子聚合
                Terms valueAgg = ((Terms.Bucket) bucket).getAggregations().get("valueAgg");

                // 把名称对应的封装到成数组
                List<String> list = valueAgg.getBuckets()
                        .stream()
                        .map(valueBucket -> ((Terms.Bucket) valueBucket).getKeyAsString())
                        .collect(Collectors.toList());

                map.put(key,list);

            });

            return map;

        });

    }

}
